/****************************************************************************

 Copyright (C) 2009 Jean Pierre Charalambos. All rights reserved.

 Jean Pierre Charalambos (http://dis.unal.edu.co/profesores/pierre/) is an
 Affiliate Associate Professor of the Faculty of Engineering at National
 University of Colombia (http://www.unal.edu.co/).

 This file is part of maku version 0.1.

 http://code.google.com/p/maku/
 email: jpcharalambosh@unal.edu.co

 This file may be used under the terms of the GNU General Public License 
 versions 2.0 or 3.0 as published by the Free Software Foundation and
 appearing in the COPYING file included in the packaging of this file.

 This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

*****************************************************************************/

#ifndef BOUNDINGBOX_H
#define BOUNDINGBOX_H

#include <QGLViewer/qglviewer.h>
#include <QVector>

using namespace OpenMesh;
using namespace qglviewer;
using namespace std;

class Mesh;

typedef QVector<Vec3f> Quads;

class BoundingBox {
public:
    BoundingBox() {
      bbMin[0] = 0; bbMin[1] = 0; bbMin[2] = 0; bbMax[0] = 1; bbMax[1] = 1; bbMax[2] = 1; setAll();
    };
    BoundingBox(const Vec3f & pmin, const Vec3f & pmax) {
      set(pmin, pmax);
    };
    virtual ~BoundingBox() {cornerV.clear(); quadsV.clear();}
    inline const QVector<Vec3f>& getCorners() const { return cornerV; }
    inline const QVector<Quads> getQuads() const { return quadsV; }
    float getDeltaX() const {return bbMax[0] - bbMin[0];}
    float getDeltaY() const {return bbMax[1] - bbMin[1];}
    float getDeltaZ() const {return bbMax[2] - bbMin[2];}
    const Vec3f getCenter() const {
      Vec3f center;
      center[0] = bbMin[0] + getDeltaX() / 2;
      center[1] = bbMin[1] + getDeltaY() / 2;
      center[0] = bbMin[2] + getDeltaZ() / 2;
      return center;
    }
    const Vec3f& getBBMin() const { return bbMin; }
    const Vec3f& getBBMax() const { return bbMax; }
    virtual void setFromMesh( const Mesh & ) {};
    void set( const Vec3f & v1, const Vec3f & v2 ) {
      bbMin = v1;
      bbMax = v2;
      setAll();
    };
    inline bool isIn( const Camera* camera ) const {
      return isIn(camera->position().x, camera->position().y, camera->position().z);
    }
    inline bool isIn( const Vec3f & mP ) const {
      if ( ((mP[0] >=  bbMin[0]) && ((mP[0] <= bbMax[0])))
        && ((mP[1] >=  bbMin[1]) && ((mP[1] <= bbMax[1])))
        && ((mP[2] >=  bbMin[2]) && ((mP[2] <= bbMax[2])))  ) {
           return true;
      }
      return false;
    }
    inline bool isIn( const float x, const float y, const float z ) const {
      if ( ((x >=  bbMin[0]) && ((x <= bbMax[0])))
        && ((y >=  bbMin[1]) && ((y <= bbMax[1])))
        && ((z >=  bbMin[2]) && ((z <= bbMax[2])))  ) {
           return true;
      }
      return false;
    }
    void draw() const {
      GLfloat curColor[4];
      glGetFloatv(GL_CURRENT_COLOR, curColor);
      float red = 1, green = 0, blue = 0;
      glColor4f(red, green, blue, 0.2f);
      glDisable(GL_LIGHTING);
      //alternativa 1
      //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      //alternativa 2
      /**
      glEnable(GL_POLYGON_STIPPLE);
      GLubyte data[32*4];
      for(int i=0;i<32*4;++i) data[i]=rand();
      glPolygonStipple( data );
      // */
      //alternativa 3
      // /**
      glEnable(GL_BLEND);
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      glDepthMask(GL_FALSE);
      // */

      QVector<Quads>::const_iterator v_it;
      Quads::const_iterator q_it;
      glBegin(GL_QUADS);
      for( v_it = getQuads().begin();
        v_it != getQuads().end(); v_it++ ) {
        for( q_it = (*v_it).begin(); q_it != (*v_it).end(); q_it++ ) {
          glVertex3fv(&(*q_it)[0]);
        }
      }
      glEnd();
      //alternativa 1:
      //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
      //alternativa 2:
      //glDisable(GL_POLYGON_STIPPLE);
      //alternativa 3:
      // /**
      glDisable(GL_BLEND);
      glDepthMask(GL_TRUE);
      // */
      //
      glColor4fv(curColor);
      glEnable(GL_LIGHTING);
    }

protected:
    Vec3f bbMin, bbMax;
    QVector<Vec3f>  cornerV;
    QVector<Quads>  quadsV;
    void setAll() {
      //1. corner:
      Vec3f jocker;
      cornerV.clear();
      cornerV.push_back(bbMin);//at 0
      cornerV.push_back(bbMax);//at 1
      jocker[0] = bbMax[0]; jocker[1] = bbMin[1]; jocker[2] = bbMin[2];
      cornerV.push_back(jocker);//a. at 2 
      jocker[0] = bbMax[0]; jocker[1] = bbMax[1]; jocker[2] = bbMin[2];
      cornerV.push_back(jocker);//b. at 3
      jocker[0] = bbMin[0]; jocker[1] = bbMax[1]; jocker[2] = bbMin[2];
      cornerV.push_back(jocker);//c. at 4
      jocker[0] = bbMax[0]; jocker[1] = bbMin[1]; jocker[2] = bbMax[2];
      cornerV.push_back(jocker);//d. at 5
      jocker[0] = bbMin[0]; jocker[1] = bbMin[1]; jocker[2] = bbMax[2];
      cornerV.push_back(jocker);//e. at 6
      jocker[0] = bbMin[0]; jocker[1] = bbMax[1]; jocker[2] = bbMax[2];
      cornerV.push_back(jocker);//f. at 7
      //2. quads:
      quadsV.clear();
      Quads quads;
      //face 0:
      quads.clear();
      quads.push_back(cornerV.at(1));
      quads.push_back(cornerV.at(5));
      quads.push_back(cornerV.at(2));
      quads.push_back(cornerV.at(3));
      quadsV.push_back(quads);
      //face 1:
      quads.clear();
      quads.push_back(cornerV.at(0));
      quads.push_back(cornerV.at(4));
      quads.push_back(cornerV.at(3));
      quads.push_back(cornerV.at(2));
      quadsV.push_back(quads);
      //face 2:
      quads.clear();
      quads.push_back(cornerV.at(0));
      quads.push_back(cornerV.at(6));
      quads.push_back(cornerV.at(7));
      quads.push_back(cornerV.at(4));
      quadsV.push_back(quads);
      //face 3:
      quads.clear();
      quads.push_back(cornerV.at(1));
      quads.push_back(cornerV.at(7));
      quads.push_back(cornerV.at(6));
      quads.push_back(cornerV.at(5));
      quadsV.push_back(quads);
      //face 4:
      quads.clear();
      quads.push_back(cornerV.at(0));
      quads.push_back(cornerV.at(2));
      quads.push_back(cornerV.at(5));
      quads.push_back(cornerV.at(6));
      quadsV.push_back(quads);
      //face 5:
      quads.clear();
      quads.push_back(cornerV.at(1));
      quads.push_back(cornerV.at(3));
      quads.push_back(cornerV.at(4));
      quads.push_back(cornerV.at(7));
      quadsV.push_back(quads);
    }
};

#endif
